home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Tools & Utilities
/
Collection of Tools and Utilities.iso
/
archiver
/
pdtar.zip
/
EXTRACT.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-05-15
|
11KB
|
389 lines
/*
* Extract files from a tar archive.
*
* Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
* MS-DOS port 2/87 by Eric Roskos.
* Minix port 3/88 by Eric Roskos.
*
* @(#) extract.c 1.17 86/10/29 Public Domain - gnu
*/
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef BSD42
#include <sys/file.h>
#endif
#ifdef USG
#include <fcntl.h>
#endif
extern int errno; /* From libc.a */
extern char *index(); /* From libc.a or port.c */
#include "tar.h"
extern union record *head; /* Points to current tape header */
extern struct stat hstat[1]; /* Stat struct corresponding */
extern struct stat *phstat; /* overcome original highly arcane C syntax */
extern void print_header();
extern void skip_file();
extern void pr_mkdir();
int make_dirs(); /* Makes required directories */
time_t now = 0; /* Current time */
/*
* Extract a file from the archive.
* Note: xname is the "fixed name" which is supposed to be acceptable
* to the OS. We use this instead of the name in the header, but only
* if we're actually creating a data file.
*/
void
extract_archive(xname)
char *xname;
{
register char *data;
int fd, check, namelen, written;
long size;
time_t acc_upd_times[2];
int standard; /* Is header standard? */
struct stat st;
saverec(&head); /* Make sure it sticks around */
userec(head); /* And go past it in the archive */
decode_header(head, hstat, &standard, 1); /* Snarf fields */
/* Print the record from 'head' and 'hstat' */
if (f_verbose)
print_header(xname);
switch (head->header.linkflag)
{
default:
annofile(stderr, tar);
fprintf(stderr, "Unknown file type %d for %s\n",
head->header.linkflag, head->header.name);
/* FALL THRU */
case LF_OLDNORMAL:
case LF_NORMAL:
/*
* Appears to be a file. See if it's really a directory.
*/
namelen = strlen(head->header.name) - 1;
if (head->header.name[namelen] == '/')
goto really_dir;
/* FIXME, deal with protection issues */
/* FIXME, f_keep doesn't work on V7, st_mode loses too */
again_file:
#ifdef V7
fd = creat(xname, phstat->st_mode);
#else
fd = open( /* head->header.name */ xname,
f_keep ?
O_NDELAY | O_WRONLY | O_APPEND | O_CREAT |
O_EXCL | convmode(xname) :
O_NDELAY | O_WRONLY | O_APPEND | O_CREAT | O_TRUNC |
convmode(xname),
phstat->st_mode);
#endif
if (fd < 0)
{
if (make_dirs(head->header.name, /* use original name here */
from_oct(8, head->header.uid),
from_oct(8, head->header.gid))) /* added uid/gid - JER */
goto again_file;
annofile(stderr, tar);
fprintf(stderr, "Could not make file ");
perror( /* head->header.name */ xname);
skip_file((long) phstat->st_size);
goto quit;
}
for (size = phstat->st_size;
size > 0;
size -= written)
{
/*
* Locate data, determine max length writeable, write it, record
* that we have used the data, then check if the write worked.
*/
data = findrec()->charptr;
written = endofrecs()->charptr - data;
if (written > size)
written = size;
errno = 0;
check = write(fd, data, written);
/*
* The following is in violation of strict typing, since the arg
* to userec should be a struct rec *. FIXME.
*/
userec(data + written - 1);
if (check == written)
continue;
/*
* Error in writing to file. Print it, skip to next file in
* archive.
*/
annofile(stderr, tar);
fprintf(stderr,
"Tried to write %d bytes to file, could only write %d:\n",
written, check);
perror( /* head->header.name */ xname);
(void) close(fd);
skip_file((long) (size - written));
goto quit;
}
check = close(fd);
if (check < 0)
{
annofile(stderr, tar);
fprintf(stderr, "Error while closing ");
perror( /* head->header.name */ xname);
}
#ifndef MSDOS
/* deal with uid/gid FIXME: mtimes/suid */
/* FIXME - should use new name-string fields if present */
chown(xname, from_oct(8, head->header.uid),
from_oct(8, head->header.gid)); /* JER */
#endif
/*
* Set the modified time of the file.
*
* Note that we set the accessed time to "now", which is really "the
* time we started extracting files".
*/
if (!f_modified)
{
if (!now)
now = time((time_t *) 0); /* Just do it once */
acc_upd_times[0] = now; /* Accessed now */
acc_upd_times[1] = phstat->st_mtime; /* Mod'd */
if (utime( /* head->header.name */ xname, acc_upd_times) < 0)
{
annofile(stderr, tar);
perror( /* head->header.name */ xname);
}
}
/*
* If '-p' is not set, OR if the file has pretty normal mode bits, we
* can skip the chmod and save a sys call. This works because we did
* umask(0) if -p is set, so the open() that created the file will
* have set the modes properly. FIXME: I don't know what open() does
* w/UID/GID/SVTX bits. However, if we've done a chown(), they got
* reset. Also skip CHMOD for MS/DOS (at least for now) since none of
* these bits are defined.
*/
#ifndef MSDOS
if (f_use_protection
&& (phstat->st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
{
if (chmod( /* head->header.name */ xname, (int) phstat->st_mode) < 0)
{
annofile(stderr, tar);
perror( /* head->header.name */ xname);
}
}
#endif
quit:
break;
case LF_LINK:
again_link:
#ifndef MSDOS
check = link(head->header.linkname,
head->header.name);
if (check == 0)
break;
if (make_dirs(head->header.linkname,
from_oct(8, head->header.uid),
from_oct(8, head->header.gid))) /* added uid/gid - JER */
goto again_link;
annofile(stderr, tar);
fprintf(stderr, "Could not link %s to ",
head->header.name);
perror(head->header.linkname);
#else
fprintf(stderr, "Cannot link %s to %s: linking not supported by DOS\n",
head->header.name, head->header.linkname);
#endif
break;
#ifdef S_IFLNK
case LF_SYMLINK:
again_symlink:
check = symlink(head->header.linkname,
head->header.name);
/* FIXME, don't worry uid, gid, etc... */
if (check == 0)
break;
if (make_dirs(head->header.linkname,
from_oct(8, head->header.uid),
from_oct(8, head->header.gid))) /* added uid/gid - JER */
goto again_symlink;
annofile(stderr, tar);
fprintf(stderr, "Could not create symlink ");
perror(head->header.linkname);
break;
#endif
case LF_CHR:
phstat->st_mode |= S_IFCHR;
goto make_node;
#ifdef S_IFBLK
case LF_BLK:
phstat->st_mode |= S_IFBLK;
#endif
make_node:
#ifndef MSDOS
/* FIXME: constant 1024 should be #define'd fs blocksize. But,
* this only works on my Minix system anyway; on a standard
* Minix or Unix, the 3rd parameter will be ignored. */
check = mknod(head->header.name, (int) phstat->st_mode,
(int) phstat->st_dev, (int)(phstat->st_size/1024));
if (check != 0)
{
if (make_dirs(head->header.name,
from_oct(8, head->header.uid),
from_oct(8, head->header.gid))) /* added uid/gid - JER */
goto make_node;
annofile(stderr, tar);
fprintf(stderr, "Could not make special file ");
perror(head->header.name);
break;
};
#else
/* with DOS, either it's there or you've got to change driver */
annofile(stderr, tar);
fprintf(stderr, "Cannot create special file %s\n",
head->header.name);
#endif
break;
case LF_DIR:
/* Check for trailing / */
namelen = strlen(head->header.name) - 1;
really_dir:
while (namelen && head->header.name[namelen] == '/')
head->header.name[namelen--] = '\0'; /* Zap / */
/* FIXME, deal with umask */
again_dir:
#ifdef MSDOS
/*
* don't do the mkdir under MSDOS if the dir already exists. (Won't
* this give an error under Unix too? Should it? I don't think
* Unix's tar does.)
*/
if (stat(head->header.name, &st) == 0 && (st.st_mode & S_IFDIR))
check = 0;
else
#endif
check = m